Repository navigation
feat(release): signed release bundles: build and attest, sign after verify (RFC-0046) - #208
Merged
Merged
Conversation
…erify (RFC-0046) - RFC-0046 (Proposed): a deterministic release bundle per release, built and attested in a reusable workflow with no secrets, signed by a separate job only after the attestation and the statement check pass - core-contracts: release-manifest and release-statement schemas, fixtures, Rust types - scripts/release-bundle.ts: deterministic builder; release-publish-plan.ts - .github/workflows/bundle-build.yml (attest-build-provenance) and release.yml jobs bundle and bundle-sign (environment release-signing), gated on vars.RELEASE_BUNDLES, with a dry run - publish-ci.ts: tag, draft release, upload and re-check, npm, then publish (immutable releases) - docs/security/release-signing-keys.md lists the active public key cloudhal-release-2026a Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
v0.8.0 was published (immutable, no assets) after this branch was cut, so the first bundled release is the version after it. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Signed release bundles, per RFC-0046 (status: Proposed). Every release can carry a deterministic, installable bundle of the standard project. A reusable workflow with no secrets builds and attests it, and a separate job in a protected environment signs it only after the attestation and the statement check pass. Hosts and tools can then deploy a release as it is, without building it themselves, and verify that what they install is byte-for-byte what this repository published.
Releases here are immutable, so the publish order changes in this PR whether or not bundles are on: tag → draft release → upload and re-check assets → npm → publish.
v0.8.0and older keep no bundle.Changes
docs/security/release-signing-keys.md, which lists the active public keycloudhal-release-2026a.release-manifestandrelease-statementschemas, with fixtures, generated TS validators and Rust types (crates/cloudhal-schema), and a changeset.scripts/release-bundle.ts(+scripts/release-bundle/): a deterministic builder. With a fixedSOURCE_DATE_EPOCHthe same commit gives the same bytes. It writescloudhal-release-<v>.tar.gz,manifest.json(per-part digests) andrelease-statement.json(version, commit,securityRelease, digests)..github/workflows/bundle-build.yml(reusable): frozen lockfile, no Actions cache, no secrets. It attests the three files withactions/attest-build-provenanceand uploads them unsigned..github/workflows/release.yml:bundleandbundle-signjobs run only whenvars.RELEASE_BUNDLES == 'true'or in a dry run.bundle-signruns in environmentrelease-signing, with no checkout and no package manager. It runsgh attestation verify(--signer-workflow …/bundle-build.yml --source-ref refs/heads/main --source-digest $GITHUB_SHA --deny-self-hosted-runners) and checks the statement, then signs it withRELEASE_SIGNING_KEY(raw Ed25519,release-statement.sig).publishstarts only if the run was not cancelled (!cancelled()).workflow_dispatchinputdry_runuses a throwaway key and a draft that is always deleted, and publishes nothing.scripts/publish-ci.ts: the immutable-release order above, resumable on "Re-run failed jobs". It reuses the tag (refusing one that points at another commit) and the draft, and uploads only missing or wrong assets. It also checks the signature againstrelease.yml's pinned key, or against the dry run's own key.scripts/release-publish-plan.ts,scripts/release-plan.ts: the dry run and security-release flags.release.ymlorpublish-ci.ts) and recovery.Before the first bundled release (maintainer actions, not in this PR)
release-signingwith a required reviewer and deployment branches limited tomain, then add secretRELEASE_SIGNING_KEY. Do this before the first dispatch, otherwise a dry run auto-creates the environment unprotected.dry_run: true. The new publish order has never run against GitHub, and the next release uses it even with bundles off.RELEASE_BUNDLES=true.Deferred (designed in RFC-0046, not built): signed
keys.jsonand yank list, the offline root key, KMS custody,@cloudhal/release-verify, andcloudhal deploy --release.Verification
biome check(error level),size-check,font-fallbacks,mcp-tools-docanddemo-archivechecks are clean.pnpm genleaves every generated file unchanged.turbo run typecheck: 20/20 tasks.@cloudhal/cli562 tests;@cloudhal/core-contracts590 tests;cargo test -p cloudhal-schema --locked21 tests;cargo clippy --all-targets -D warningsis clean.actionlintis clean. Every action is pinned to a commit, and the tag→SHA mapping was checked forupload-artifactv7.0.2,download-artifactv8.0.2 andattest-build-provenancev4.2.2.node scripts/publish-ci.ts --plan --dry-runprints the expected plan.bundle-sign's dry-run branch with an in-memory key. The monorepo verifier accepts it with that key and refuses it with the release key, a flipped byte, the wrong commit, asecurityReleasemismatch or the wrong key id. A downstream consumer's ingest (Clavius) accepts the same bundle and refuses each of those tamperings.turbo run test --concurrency=1: 19/19 tasks pass. Underpnpm check's--concurrency=3,@cloudhal/mcp-server(not touched here) timed out at 30 s on the 8 GB development machine, while running alongsidecore-tsandstudio. It passes 183/183 alone and in the serial run.Checklist
pnpm checkandcargo test --workspacepass locally (see themcp-servernote under Verification)pnpm genwas run and generated files are committed (docs/adr/0001)pnpm changesetaddedgit commit -s, DCO)🤖 Generated with Claude Code